﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Linq.Expressions;
using System.Data.Objects;
using CoolCode.Linq.Expressions;

namespace CoolCode.Data.Entity
{
    public class Repository<T> : IRepository<T>, IQueryable<T>, IEnumerable<T>, IDisposable where T : class
    {
        private static readonly string entityName = typeof(T).Name;

        private ObjectContext db;

        private ILoadOption _loadOptions;

        internal Repository(XDataContext context)
        {
             db = context.InnerContext;
             _loadOptions = context.LoadOptions;
        }

        private ObjectQuery<T> _table = null;

        protected virtual ObjectQuery<T> table
        {
            get
            {
                if (_table == null)
                {
                    _table = db.CreateQuery<T>("[" + entityName + "]");
                    var loaders= ((XDataContext.LoadOption)_loadOptions).Loaders ;
                    foreach (var loader in loaders)
                    {
                        if (loader.Item1.Name == entityName)
                        {
                            _table = _table.Include(loader.Item2);
                        }
                    }
                }
                return _table;
            }
        }

        #region IRepository<T> Members

        public virtual void Insert(T entity)
        {
            db.AddObject(entityName, entity);
        }

        public virtual void Insert(IEnumerable<T> list)
        {
            foreach (T entity in list)
            {
                db.AddObject(entityName, entity);
            }
        }

        public virtual void Update(T entity)
        {
            db.AttachTo(entityName, entity);
        }

        //public virtual void Update(T entity, T original)
        //{
        //    db.Attach(entity, original);
        //}

		public virtual int Update(Expression<Func<T, bool>> predicate, Expression<Func<T, T>> updater)
		{
			throw new NotImplementedException("Linq To Entity not support batch update now!"); 
		}

        public virtual void Update(IEnumerable<T> list)
        {
            foreach (T entity in list)
            {
                db.AttachTo(entityName, entity);
            }
        }

        public virtual void Delete(T entity)
        {
            db.DeleteObject(entity);
        }

        public virtual void Delete(IQueryable<T> query)
        {
            foreach (T entity in query)
                db.DeleteObject(entity);
        }

        public virtual void Delete(IEnumerable<T> list)
        {
            foreach (T entity in list)
                db.DeleteObject(entity);
        }

        public virtual void Delete(Expression<Func<T, bool>> predicate)
        {
            var query = this.Where(predicate);
            foreach (T entity in query)
                db.DeleteObject(entity);
        }

        public IQueryable<T> In<P>(Expression<Func<T, P>> property, IList<P> values)
        {
            values.ThrowIfNullOrEmpty("value");
            Expression<Func<T, bool>> lambda = ExpressionTreeHelpers.BuildContainsExpression(property, values);

            return table.Where(lambda);
        }

        public virtual IQueryable<T> Query()
        {
            return table;
        }

        public virtual IQueryable<T> Query(Expression<Func<T, bool>> predicate)
        {
            return table.Where(predicate);
        }

        //IQueryable<T> IRepository<T>.Where(Expression<Func<T, bool>> predicate)
        //{
        //    return table.Where(predicate);
        //}

        //public  List<T> List(IQueryable<T> query, bool withNoLock)
        //{
        //    return db.ExecuteQuery<T>(query, withNoLock).ToList();
        //}

        public virtual T Get(Expression<Func<T, bool>> predicate)
        {
            return table.FirstOrDefault(predicate);
        }


        #endregion

        #region IEnumerable<T> Members

        public IEnumerator<T> GetEnumerator()
        {
            return ((IEnumerable<T>)table).GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        #endregion

        #region IQueryable Members

        Type IQueryable.ElementType
        {
            get
            {
                return typeof(T);
            }
        }

        Expression IQueryable.Expression
        {
            get
            {
                return ((IQueryable)table).Expression; // return Expression.Constant(table);
            }
        }

        IQueryProvider IQueryable.Provider
        {
            get { return ((IQueryable)table).Provider; }
        }

        #endregion

        #region IDisposable Members

        public virtual void Dispose()
        {
            if (db != null)
            {
                db.Dispose();
            }
        }

        #endregion
    }


}
